/**
* @auther Yuxin Ma
* @module models/graph-model
* @exports Graph
*/
'use strict';
var $ = require('jquery');
var _ = require('underscore');
var Backbone = require('backbone');
Backbone.$ = $;
var Node = require('./node-model');
var NodeCollection = require('../collections/node-collection');
var Edge = require('./edge-model');
var EdgeCollection = require('../collections/edge-collection');
var egoLoader = require('./demodata/egoloader');
var Graph = Backbone.Model.extend( /** @lends models/Graph.prototype */ {
/**
* 图结构
*
* @augments Backbone.Model
* @constructs
*/
initialize: function() {
/*
* TODO: 2015.11.06 先使用方舟的前端读取方法进行填充。等待后端完全实现后在EgoQuery中修改。
*/
},
defaults: {
/**
* 节点集合。
* @type {?NodeCollection}
*/
nodes: null,
/**
* 边集合。
* @type {?EdgeCollection}
*/
edges: null,
/**
* 图的类型。枚举量,为Graph.Type中的一种:{(EGO|PATH|COMM)}。
* @type {string}
*/
type: null,
/**
* 查询创建的日期。
* @type {?Date}
*/
queryDate: null,
/**
* 最后修改日期。
* @type {?Date}
*/
lastUpdateDate: null,
__lastVisualDiameter: null,
__lastVisualScale: 1.0
},
/**
* @method 重载parse方法,用于生成前端数据。
* @param {*} data
* @param {*} options
* @returns {*|{nodes, edges, type, nodeProperties, edgeProperties, queryDate, lastUpdateDate}}
*/
parse: function(data, options) {
//this.set('type', data.type);
if (data.DEBUG_EGO_FRONTEND === true) {
this.set('DEBUG_EGO_FRONTEND', true);
this.set('nodes', new NodeCollection());
this.set('edges', new EdgeCollection());
return this.__loadDemoEgoGraph(data.diameter);
}
},
__loadDemoEgoGraph: function(diameter) {
var nodes = new NodeCollection();
var edges = new EdgeCollection();
var ego = egoLoader(nodes, edges);
ego.readEgo(function() {
ego.layoutEgo(diameter, function(){});
});
// 添加未删除标签
nodes.each(function(model) {
model.set('__deleted', false);
});
edges.each(function(model) {
model.set('__deleted', false)
});
return {
nodes: nodes,
edges: edges,
type: Graph.Type.EGO,
nodeProperties: ['gender', 'keyFlag', 'level'],
edgeProperties: ['count', 'type'],
queryDate: new Date(),
lastUpdateDate: new Date()
};
},
/**
* 返回网络类型,为Graph.Type中的一种
* @returns {String} Graph的类型
*/
getType: function() {
return this.get('type');
},
/**
* 判断是否为Ego网络
* @return {boolean}
*/
isEgoNetwork: function() {
return this.get('type') === Graph.Type.EGO;
},
/**
* 根据id号查询Node。
* @param nodeId
* @returns {(Node|undefined)}
*/
getNodeById: function(nodeId) {
return this.get('nodes').get(nodeId);
},
/**
* 根据id号查询Edge。
* @param edgeId
* @returns {(Edge|undefined)}
*/
getEdgeById: function(edgeId) {
return this.get('edges').get(edgeId);
},
/**
* 通过设置过滤器来查找节点
* @param filterOptions 过滤参数
* @returns {Node[]} 查找到的节点数组
*/
getNodeByProperty: function(filterOptions) {
return this.get('nodes').where(filterOptions);
},
/**
* 返回节点的可用属性
* @returns {string[]}
*/
getNodeProperties: function() {
return this.get('nodeProperties');
},
/**
* 返回边的可用属性
* @returns {string[]}
*/
getEdgeProperties: function() {
return this.get('edgeProperties');
},
/**
* 生成所有可用Node的数组。在lazy模式下,会屏蔽所有已删除的节点。
* @returns {Node[]}
*/
getNodeArray: function() {
var nodes = this.get('nodes');
if (nodes === null) {
throw new Error('graph not initialized');
}
return nodes.filter({__deleted: false});
},
/**
* 生成所有可用Edge的数组。在lazy模式下,会屏蔽所有已删除的边。
* @returns {Edge[]}
*/
getEdgeArray: function() {
var edges = this.get('edges');
if (edges === null) {
throw new Error('graph not initialized');
}
return edges.filter({__deleted: false});
},
/**
* 查找邻居
* @param {Node} node - 节点id号,或Node的引用。
* @param {string} direction - 指定方向,为Graph.Direction中的一个值。
* @return {array} Node和Edge的数组。
*/
getNeighbors: function(node, direction) {
throw new Error('not implemented');
},
/**
* 添加节点
* @param {Node} node - 新节点
*/
addNode: function(node) {
this.get('nodes').add(node);
},
/**
* 添加边
* @param {Edge} edge - 新边
*/
addEdge: function(edge) {
this.get('edges').add(edge);
},
///**
// * 删除节点
// * @param {Node} node
// * @param options
// */
removeNode: function(node, options) {
throw new Error('not implemented');
},
///**
// * 删除边
// * @param {Edge} edge
// * @param options
// */
removeEdge: function(edge, options) {
throw new Error('graph not initialized');
},
///**
// * 合并节点
// * @param {Node[]} nodeArray - 需要合并的节点
// */
mergeNodes: function(nodeArray) {
throw new Error('graph not initialized');
},
/**
* 重新计算Node的位置。
* @param {object} options 与图类型相对应的视觉参数
*/
layout: function(options) {
var type = this.get('type');
/*
* 根据图的类型,检查layout所需参数是否完整。如检出错误则抛出Error对象。
*/
switch (type) {
case Graph.Type.EGO:
if (options.diameter === undefined || typeof(options.diameter) !== 'number')
throw new Error('missing diameter or wrong type');
break;
case Graph.Type.PATH:
break;
}
/*
* 测试用
* TODO: 后端完成后删除
*/
if (this.get('DEBUG_EGO_FRONTEND') === true) {
this.set(this.__loadDemoEgoGraph(options.diameter));
this.set('__lastVisualDiameter', options.diameter);
}
},
/**
* 返回是否填充过后端布局数据。
* @returns {boolean}
*/
hasServerSideLayout: function() {
return true;
},
///**
// * 对整个Graph的位置进行放缩。
// * @param options {Object} 与图类型相对应的视觉参数:
// * {
// * scale: {number} 放大系数。1.0为原大。
// * }
// */
scale: function(options) {
/**
* TODO: 将scale方法放到graphvis.layout.ego中
*/
throw new Error('method not implemented');
//if (this.get('DEBUG_EGO_FRONTEND') === true) {
// console.log(this.get('__lastVisualDiameter'));
// console.log(options.scale);
// console.log(options.scale * this.get('__lastVisualDiameter'));
//
// // 2015.11.30 修改:不能直接使用__loadDemoEgoGraph方法创建新的NodeCollection实例,而是获取新结果后手动set原NodeCollection。
// //this.set(this.__loadDemoEgoGraph(options.scale * this.get('__lastVisualDiameter')));
// var scaledResult = this.__loadDemoEgoGraph(options.scale * this.get('__lastVisualDiameter'));
// var oldNodes = this.get('nodes');
//
// scaledResult.nodes.each(function(d) {
// oldNodes.get(d.id).set({x: d.get('x'), y: d.get('y')});
// });
//
// this.set('__lastVisualScale', options.scale);
// this.set('lastUpdateDate', new Date());
//}
},
getScale: function() {
return this.get('__lastVisualScale');
}
}, {
// 静态常量成员
/**
* 边的方向:{(IN|OUT|BOTH)}
*/
Direction: {
IN: 'in',
OUT: 'out',
BOTH: 'both'
},
/**
* Graph的类型:{(EGO|PATH|COMM)}
*/
Type: {
EGO: 'ego',
PATH: 'path',
COMM: 'comm'
}
});
module.exports = Graph;